using System;
using System.Collections.Generic;
using System.Text;

using NUnit.Framework;

using NSimpleDB.Service.VistaDb.QueryProcessing.Compiler;
using NSimpleDB.Service.VistaDb.QueryProcessing.Compiler.AST;

namespace test.Service.VistaDb.Whitebox
{
    [TestFixture]
    public class test_Query_Parser
    {
        [Test]
        public void testSinglePredicate()
        {
            QueryParser p = new QueryParser();
            Query q = p.Parse("['a'='av']");

            Assert.AreEqual(1, q.ItemSetTermList.Length);
            Assert.AreEqual(1, q.ItemSetTermList[0].ItemSetFactorList.Length);
            Assert.AreEqual(1, q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList.Length);
            Assert.AreEqual(1, q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[0].PredicateFactorList.Length);
            Assert.AreEqual("'a'", q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[0].PredicateFactorList[0].Comparison.AttributeName);
            Assert.AreEqual(QueryTokensTypes.EqualOp, q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[0].PredicateFactorList[0].Comparison.ComparisonOp);
            Assert.AreEqual("'av'", q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[0].PredicateFactorList[0].Comparison.AttributeValue);
        }


        [Test]
        public void testAND()
        {
            QueryParser p = new QueryParser();
            Query q = p.Parse("['a'='av' and 'a' != 'av2']");

            Assert.AreEqual(1, q.ItemSetTermList.Length);
            Assert.AreEqual(1, q.ItemSetTermList[0].ItemSetFactorList.Length);
            Assert.AreEqual(1, q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList.Length);
            Assert.AreEqual(2, q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[0].PredicateFactorList.Length);
            Assert.AreEqual("'a'", q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[0].PredicateFactorList[0].Comparison.AttributeName);
            Assert.AreEqual(QueryTokensTypes.EqualOp, q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[0].PredicateFactorList[0].Comparison.ComparisonOp);
            Assert.AreEqual("'av'", q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[0].PredicateFactorList[0].Comparison.AttributeValue);
            Assert.AreEqual("'a'", q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[0].PredicateFactorList[1].Comparison.AttributeName);
            Assert.AreEqual(QueryTokensTypes.NotEqualOp, q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[0].PredicateFactorList[1].Comparison.ComparisonOp);
            Assert.AreEqual("'av2'", q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[0].PredicateFactorList[1].Comparison.AttributeValue);

            q = p.Parse("['a'='av' and 'a' != 'av2' and 'a' >= 'av3']");
            Assert.AreEqual(3, q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[0].PredicateFactorList.Length);
        }

        
        [Test]
        public void testOR()
        {
            QueryParser p = new QueryParser();
            Query q = p.Parse("['a'='av' or 'a' != 'av2']");

            Assert.AreEqual(1, q.ItemSetTermList.Length);
            Assert.AreEqual(1, q.ItemSetTermList[0].ItemSetFactorList.Length);
            Assert.AreEqual(2, q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList.Length);
            Assert.AreEqual(1, q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[0].PredicateFactorList.Length);
            Assert.IsFalse(q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[0].PredicateFactorList[0].Negate);
            Assert.AreEqual("'a'", q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[0].PredicateFactorList[0].Comparison.AttributeName);
            Assert.AreEqual(QueryTokensTypes.EqualOp, q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[0].PredicateFactorList[0].Comparison.ComparisonOp);
            Assert.AreEqual("'av'", q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[0].PredicateFactorList[0].Comparison.AttributeValue);

            Assert.AreEqual(1, q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[1].PredicateFactorList.Length);
            Assert.AreEqual("'a'", q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[1].PredicateFactorList[0].Comparison.AttributeName);
            Assert.AreEqual(QueryTokensTypes.NotEqualOp, q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[1].PredicateFactorList[0].Comparison.ComparisonOp);
            Assert.AreEqual("'av2'", q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[1].PredicateFactorList[0].Comparison.AttributeValue);

            q = p.Parse("['a'='av' or 'a' != 'av2' or 'a' >= 'av3']");
            Assert.AreEqual(3, q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList.Length);
        }


        [Test]
        public void testNotInPredicate()
        {
            QueryParser p = new QueryParser();
            Query q = p.Parse("[not 'a'='av' or 'a' != 'av2' and not 'a' != 'av3']");

            Assert.IsTrue(q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[0].PredicateFactorList[0].Negate);
            Assert.IsFalse(q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[1].PredicateFactorList[0].Negate);
            Assert.IsTrue(q.ItemSetTermList[0].ItemSetFactorList[0].Predicate.PredicateTermList[1].PredicateFactorList[1].Negate);
        }


        [Test]
        public void testWithErrors()
        {
            // syntactic error: improper query termination
            try
            {
                QueryParser p = new QueryParser();
                Query q = p.Parse("['a' = 'av'] x");
                Assert.Fail();
            }
            catch (QueryCompilerException ex)
            {
                Assert.AreEqual(QueryCompilerException.ErrorUnexpectedEndOfQuery, ex.ErrorCode);
            }

            // syntactic error: unexpected/missing symbol
            try
            {
                QueryParser p = new QueryParser();
                Query q = p.Parse("['a'  'av']");
                Assert.Fail();
            }
            catch (QueryCompilerException ex)
            {
                Assert.AreEqual(QueryCompilerException.ErrorInvalidSymbol, ex.ErrorCode);
            }


            // semantic error: multiple attribute names in predicate
            try
            {
                QueryParser p = new QueryParser();
                Query q = p.Parse("['a' = 'av' and 'b' = 'bv']");
                Assert.Fail();
            }
            catch (QueryCompilerException ex)
            {
                Assert.AreEqual(QueryCompilerException.ErrorInSemantics, ex.ErrorCode);
            }
        }


        [Test]
        public void testINTERSECTION()
        {
            QueryParser p = new QueryParser();

            Query q = p.Parse("['a'='av'] intersection ['b'='bv']");
            Assert.AreEqual(1, q.ItemSetTermList.Length);
            Assert.AreEqual(2, q.ItemSetTermList[0].ItemSetFactorList.Length);

            q = p.Parse("['a'='av'] intersection ['b'='bv'] intersection ['c'='cv']");
            Assert.AreEqual(3, q.ItemSetTermList[0].ItemSetFactorList.Length);
        }


        [Test]
        public void testUNION()
        {
            QueryParser p = new QueryParser();
            
            Query q = p.Parse("['a'='av'] union ['b'='bv']");
            Assert.AreEqual(2, q.ItemSetTermList.Length);

            q = p.Parse("['a'='av'] union ['b'='bv'] union ['c'='cv']");
            Assert.AreEqual(3, q.ItemSetTermList.Length);
        }


        [Test]
        public void testMixedSetOps()
        {
            QueryParser p = new QueryParser();

            Query q = p.Parse("['a'='av'] union ['b'='bv'] intersection ['c'='cv'] union ['d'='dv']");
            Assert.AreEqual(3, q.ItemSetTermList.Length);
            Assert.AreEqual(1, q.ItemSetTermList[0].ItemSetFactorList.Length);
            Assert.AreEqual(2, q.ItemSetTermList[1].ItemSetFactorList.Length);
            Assert.AreEqual(1, q.ItemSetTermList[2].ItemSetFactorList.Length);
        }


        [Test]
        public void testNotForPredicate()
        {
            QueryParser p = new QueryParser();

            Query q = p.Parse("not ['a'='av'] union ['b'='bv'] intersection not ['c'='cv']");
            Assert.IsTrue(q.ItemSetTermList[0].ItemSetFactorList[0].Negate);
            Assert.IsFalse(q.ItemSetTermList[1].ItemSetFactorList[0].Negate);
            Assert.IsTrue(q.ItemSetTermList[1].ItemSetFactorList[1].Negate);
        }
    }
}
